//
//	(c) 2009 Andrew Kalmacky
//

#ifndef _AK_NON_ATOMIC_H_
#define _AK_NON_ATOMIC_H_

namespace ak
{
	inline void relax_cpu() {}
}

namespace ak
{
	template<typename BASE> inline void should_be_convertible_to(const BASE& b)
	{
	}

	namespace non_atomic
	{
		namespace this_thread
		{
			inline void yield() {}
		}
		enum memory_order
		{ 
			memory_order_relaxed,
			memory_order_acquire,
			memory_order_consume = memory_order_acquire,
			memory_order_release, 
			memory_order_acq_rel,
			memory_order_seq_cst = memory_order_acq_rel,
		};

		template<typename T> struct atomic
		{
			T val;

			atomic()
				: val()
			{
			}
			~atomic()
			{
			}
			atomic(const atomic<T>& s)
				: val(s.val)
			{
			}
			atomic(const T& v)
				: val(v)
			{
			}
			atomic& operator= (const atomic<T>& s)
			{
				store(s.load(memory_order_seq_cst), memory_order_seq_cst);
				return *this;
			}

			//
			// ----- load & store -------
			//
			operator T () const { return load(); }
			atomic<T>& operator= (const T& v)
			{ 
				store(v, memory_order_acq_rel); 
				return *this; 
			}
			void store(const T& v, memory_order mo = memory_order_release) throw()
			{
				val = v;
			}
			T load(memory_order mo = memory_order_acquire) const
			{
				return val;
			}

			T exchange(const T& v, memory_order mo = memory_order_acq_rel) throw()
			{
				T temp = val;
				val = v;
				return temp;
			}

			bool compare_exchange_strong(T& expected_and_result, const T& new_val, memory_order order = memory_order_seq_cst) throw()
			{
				return compare_exchange_strong(expected_and_result, new_val, order, memory_order_relaxed);
			}
			bool compare_exchange_strong(T& expected_and_result, const T& new_val, memory_order success_order, memory_order failure_order) throw()
			{
				if (val != expected_and_result)
				{
					expected_and_result = val;
					return false;
				}
				else
				{
					val = new_val;
					return true;
				}
			}
			bool compare_exchange_weak(T& expected_and_result, const T& new_val, memory_order order = memory_order_seq_cst) throw()
			{
				return compare_exchange_strong(expected_and_result, new_val, order);
			}
			bool compare_exchange_weak(T& expected_and_result, const T& new_val, memory_order success_order, memory_order failure_order = memory_order_seq_cst) throw()
			{
				return compare_exchange_strong(expected_and_result, new_val, success_order, failure_order);
			}

			//
			// ----- x_op -------
			//
			T operator+= (int d) { return val += d; }
			T operator-= (int d) { return val -= d;	}
			T operator&= (int d) { return val &= d;	}
			T operator|= (int d) { return val |= d;	}
			T operator^= (int d) { return val ^= d;	}

			T fetch_add(int t, memory_order order = memory_order_seq_cst) throw()
			{
				return (val += t) - t;
			}
			T fetch_sub(int t, memory_order order = memory_order_seq_cst) throw()
			{
				return fetch_add(-t, order);
			}
			T fetch_or(const T& t, memory_order order = memory_order_seq_cst) throw()
			{
				T r = val;
				val |= t;
				return r;
			}
			T fetch_and(const T& t, memory_order order = memory_order_seq_cst) throw()
			{
				T r = val;
				val &= t;
				return r;
			}
			T fetch_xor(const T& t, memory_order order = memory_order_seq_cst) throw()
			{
				return (val ^= t) ^ t;
			}

			//
			// ----- inc -------
			//
			T operator++ (int) { return val++; }
			T operator++ () { return ++val; }
			T operator-- (int) { return val--; }
			T operator-- () { return --val; }

			//
			// ----- ptrs -------
			//
			T& operator-> ()
			{
				return val;
			}
			operator T ()
			{
				return val;
			}

			//
			// ----- type casts -----
			//
			template<typename BASE> operator atomic<BASE>&()
			{
				should_be_convertible_to<BASE>(static_cast<T>(NULL));
				return reinterpret_cast<atomic<BASE>&>(*this);
			}
			template<typename DESCENDANT> atomic<DESCENDANT>& cast()
			{
				should_be_convertible_to<T*>(static_cast<DESCENDANT*>(NULL));
				return reinterpret_cast<atomic<DESCENDANT>&>(*this);
			}
		};
	}
}

#endif // _AK_NON_ATOMIC_H_
